#define DPRINTK(_f, _a...) ((void)0)
#endif
-
/* XXX SMH: crappy 'hash function' .. fix when care. */
#define HSH(_x) (((_x) >> 6) & (VBD_HTAB_SZ - 1))
** Create a new VBD; all this involves is adding an entry to the domain's
** vbd hash table; caller must be privileged.
*/
-long vbd_create(vbd_create_t *create_params)
+long vbd_create(vbd_create_t *create)
{
struct task_struct *p;
- vbd_t *new_vbd, *v;
- int h;
+ vbd_t *new_vbd, **pv;
+ long ret = 0;
- if(!IS_PRIV(current))
- return -EPERM;
+ if( !IS_PRIV(current) )
+ return -EPERM;
- p = find_domain_by_id(create_params->domain);
+ if ( (p = find_domain_by_id(create->domain)) == NULL )
+ {
+ DPRINTK("vbd_create attempted for non-existent domain %d\n",
+ create->domain);
+ return -EINVAL;
+ }
- if (!p) {
- printk("vbd_create attempted for non-existent domain %d\n",
- create_params->domain);
- return -EINVAL;
+ spin_lock(&p->vbd_lock);
+
+ for ( pv = &p->vbdtab[HSH(create->vdevice)];
+ *pv != NULL;
+ pv = &(*pv)->next )
+ {
+ if ( (*pv)->vdevice == create->vdevice )
+ {
+ DPRINTK("vbd_create attempted for already existing vbd\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ if ( (*pv)->vdevice > create->vdevice )
+ break;
}
new_vbd = kmalloc(sizeof(vbd_t), GFP_KERNEL);
- new_vbd->vdevice = create_params->vdevice;
- new_vbd->mode = create_params->mode;
+ new_vbd->vdevice = create->vdevice;
+ new_vbd->mode = create->mode;
new_vbd->extents = (xen_extent_le_t *)NULL;
new_vbd->next = (vbd_t *)NULL;
- h = HSH(create_params->vdevice);
- if(p->vbdtab[h]) {
- for(v = p->vbdtab[h]; v->next; v = v->next)
- ;
- v->next = new_vbd;
- } else p->vbdtab[h] = new_vbd;
+ *pv = new_vbd;
+ out:
+ spin_unlock(&p->vbd_lock);
put_task_struct(p);
-
- return 0;
+ return ret;
}
/*
** Add an extent to an existing VBD; fails if the VBD doesn't exist.
** Doesn't worry about overlapping extents (e.g. merging etc) for now.
*/
-long vbd_add(vbd_add_t *add_params)
+long vbd_add(vbd_add_t *add)
{
struct task_struct *p;
- xen_extent_le_t *x, *xele;
+ xen_extent_le_t **px, *x;
vbd_t *v;
- int h;
-
- if(!IS_PRIV(current))
- return -EPERM;
+ long ret = 0;
- p = find_domain_by_id(add_params->domain);
+ if ( !IS_PRIV(current) )
+ return -EPERM;
- if (!p) {
- printk("vbd_add attempted for non-existent domain %d\n",
- add_params->domain);
- return -EINVAL;
+ if ( (p = find_domain_by_id(add->domain)) == NULL )
+ {
+ DPRINTK("vbd_add attempted for non-existent domain %d\n",
+ add->domain);
+ return -EINVAL;
}
- h = HSH(add_params->vdevice);
+ spin_lock(&p->vbd_lock);
- for(v = p->vbdtab[h]; v; v = v->next)
- if(v->vdevice == add_params->vdevice)
- break;
+ for ( v = p->vbdtab[HSH(add->vdevice)]; v != NULL; v = v->next )
+ if ( v->vdevice == add->vdevice )
+ break;
- if(!v) {
- printk("vbd_add; attempted to add extent to non-existent VBD.\n");
- return -EINVAL;
+ if ( v == NULL )
+ {
+ DPRINTK("vbd_add; attempted to add extent to non-existent VBD.\n");
+ ret = -EINVAL;
+ goto out;
}
- xele = kmalloc(sizeof(xen_extent_le_t), GFP_KERNEL);
- xele->extent.device = add_params->extent.device;
- xele->extent.start_sector = add_params->extent.start_sector;
- xele->extent.nr_sectors = add_params->extent.nr_sectors;
- xele->next = (xen_extent_le_t *)NULL;
-
- if(!v->extents) {
- v->extents = xele;
- } else {
- for(x = v->extents; x->next; x = x->next)
- ;
- x->next = xele;
- }
+ x = kmalloc(sizeof(xen_extent_le_t), GFP_KERNEL);
+ x->extent.device = add->extent.device;
+ x->extent.start_sector = add->extent.start_sector;
+ x->extent.nr_sectors = add->extent.nr_sectors;
+ x->next = (xen_extent_le_t *)NULL;
- put_task_struct(p);
+ for ( px = &v->extents; *px != NULL; px = &(*px)->next )
+ continue;
+
+ *px = x;
- return 0;
+ out:
+ spin_unlock(&p->vbd_lock);
+ put_task_struct(p);
+ return ret;
}
-long vbd_remove(vbd_remove_t *remove_params)
+long vbd_remove(vbd_remove_t *remove)
{
- if(!IS_PRIV(current))
- return -EPERM;
+ struct task_struct *p;
+ xen_extent_le_t **px, *x;
+ vbd_t *v;
+ long ret = 0;
+
+ if ( !IS_PRIV(current) )
+ return -EPERM;
+
+ if ( (p = find_domain_by_id(remove->domain)) == NULL )
+ {
+ DPRINTK("vbd_remove attempted for non-existent domain %d\n",
+ remove->domain);
+ return -EINVAL;
+ }
+
+ spin_lock(&p->vbd_lock);
+
+ for ( v = p->vbdtab[HSH(remove->vdevice)]; v != NULL; v = v->next )
+ if ( v->vdevice == remove->vdevice )
+ break;
+
+ if ( v == NULL )
+ {
+ DPRINTK("vbd_remove; attempt to remove ext from non-existent VBD.\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for ( px = &v->extents; *px != NULL; px = &(*px)->next )
+ if ( (*px)->extent.start_sector == remove->extent.start_sector )
+ break;
+
+ if ( ((x = *px) == NULL) ||
+ (x->extent.nr_sectors != remove->extent.nr_sectors) ||
+ (x->extent.device != remove->extent.device) )
+ {
+ DPRINTK("vbd_remove: attempt to remove non-matching extent.\n");
+ ret = -EINVAL;
+ goto out;
+ }
- return -ENOSYS;
+ *px = x->next;
+ kfree(x);
+
+ out:
+ spin_unlock(&p->vbd_lock);
+ put_task_struct(p);
+ return ret;
}
-long vbd_delete(vbd_delete_t *delete_params)
+long vbd_delete(vbd_delete_t *delete)
{
- if(!IS_PRIV(current))
- return -EPERM;
+ struct task_struct *p;
+ vbd_t *v, **pv;
+ xen_extent_le_t *x, *t;
+
+ if( !IS_PRIV(current) )
+ return -EPERM;
+
+ if ( (p = find_domain_by_id(delete->domain)) == NULL )
+ {
+ DPRINTK("vbd_delete attempted for non-existent domain %d\n",
+ delete->domain);
+ return -EINVAL;
+ }
+
+ spin_lock(&p->vbd_lock);
+
+ for ( pv = &p->vbdtab[HSH(delete->vdevice)];
+ *pv != NULL;
+ pv = &(*pv)->next )
+ {
+ if ( (*pv)->vdevice == delete->vdevice )
+ goto found;
+ }
+
+ DPRINTK("vbd_delete attempted for non-existing VBD.\n");
- return -ENOSYS;
+ spin_unlock(&p->vbd_lock);
+ put_task_struct(p);
+ return -EINVAL;
+
+ found:
+ v = *pv;
+ *pv = v->next;
+ x = v->extents;
+ kfree(v);
+
+ while ( x != NULL )
+ {
+ t = x->next;
+ kfree(x);
+ x = t;
+ }
+
+ spin_unlock(&p->vbd_lock);
+ put_task_struct(p);
+ return 0;
+}
+
+
+void destroy_all_vbds(struct task_struct *p)
+{
+ int i;
+ vbd_t *v;
+ xen_extent_le_t *x, *t;
+
+ spin_lock(&p->vbd_lock);
+ for ( i = 0; i < VBD_HTAB_SZ; i++ )
+ {
+ while ( (v = p->vbdtab[i]) != NULL )
+ {
+ p->vbdtab[i] = v->next;
+
+ x = v->extents;
+ kfree(v);
+
+ while ( x != NULL )
+ {
+ t = x->next;
+ kfree(x);
+ x = t;
+ }
+ }
+ }
+ spin_unlock(&p->vbd_lock);
}
xen_extent_le_t *x;
xen_disk_t cur_disk;
vbd_t *v;
- int i, ret;
-
- for(i = 0; i < VBD_HTAB_SZ; i++) {
-
- for(v = p->vbdtab[i]; v; v = v->next) {
-
- /* SMH: don't ever expect this to happen, hence verbose printk */
- if ( xdi->count == xdi->max ) {
- printk("vbd_probe_devices: out of space for probe.\n");
- return -ENOMEM;
- }
-
- cur_disk.device = v->vdevice;
- cur_disk.info = XD_FLAG_VIRT | XD_TYPE_DISK;
- if(!VBD_CAN_WRITE(v))
- cur_disk.info |= XD_FLAG_RO;
- cur_disk.capacity = 0 ;
- for(x = v->extents; x; x = x->next)
- cur_disk.capacity += x->extent.nr_sectors;
- cur_disk.domain = p->domain;
-
- /* Now copy into relevant part of user-space buffer */
- if((ret = copy_to_user(xdi->disks + xdi->count, &cur_disk,
- sizeof(xen_disk_t))) < 0) {
- printk("vbd_probe_devices: copy_to_user failed [rc=%d]\n",
- ret);
- return ret;
- }
-
-
- xdi->count++;
- }
+ int i;
+
+ spin_lock(&p->vbd_lock);
+
+ for ( i = 0; i < VBD_HTAB_SZ; i++ )
+ {
+ for ( v = p->vbdtab[i]; v != NULL; v = v->next )
+ {
+ if ( xdi->count == xdi->max )
+ {
+ DPRINTK("vbd_probe_devices: out of space for probe.\n");
+ spin_unlock(&p->vbd_lock);
+ return -ENOMEM;
+ }
+
+ cur_disk.device = v->vdevice;
+ cur_disk.info = XD_FLAG_VIRT | XD_TYPE_DISK;
+ if ( !VBD_CAN_WRITE(v) )
+ cur_disk.info |= XD_FLAG_RO;
+ cur_disk.capacity = 0 ;
+ for ( x = v->extents; x != NULL; x = x->next )
+ cur_disk.capacity += x->extent.nr_sectors;
+ cur_disk.domain = p->domain;
+
+ /* Now copy into relevant part of user-space buffer */
+ if( copy_to_user(&xdi->disks[xdi->count],
+ &cur_disk,
+ sizeof(xen_disk_t)) )
+ {
+ DPRINTK("vbd_probe_devices: copy_to_user failed\n");
+ spin_unlock(&p->vbd_lock);
+ return -EFAULT;
+ }
+
+ xdi->count++;
+ }
}
+ spin_unlock(&p->vbd_lock);
return 0;
}
** all domains ("VBD_PROBE_ALL") -- both of these cases require the
** caller to be privileged.
*/
-long vbd_probe(vbd_probe_t *probe_params)
+long vbd_probe(vbd_probe_t *probe)
{
struct task_struct *p = NULL;
- short putp = 0;
- int ret = 0;
-
- if(probe_params->domain) {
-
- /* we can only probe for ourselves unless we're privileged */
- if(probe_params->domain != current->domain && !IS_PRIV(current))
- return -EPERM;
-
- if(probe_params->domain != VBD_PROBE_ALL) {
-
- p = find_domain_by_id(probe_params->domain);
-
- if (!p) {
- printk("vbd_probe attempted for non-existent domain %d\n",
- probe_params->domain);
- return -EINVAL;
- }
-
- putp = 1;
- }
-
- } else
- /* default is to probe for ourselves */
- p = current;
-
-
- if(!p || IS_PRIV(p)) {
+ unsigned long flags;
+ long ret = 0;
+
+ if ( probe->domain != 0 )
+ {
+ /* We can only probe for ourselves unless we're privileged. */
+ if( (probe->domain != current->domain) && !IS_PRIV(current) )
+ return -EPERM;
+
+ if ( (probe->domain != VBD_PROBE_ALL) &&
+ ((p = find_domain_by_id(probe->domain)) == NULL) )
+ {
+ DPRINTK("vbd_probe attempted for non-existent domain %d\n",
+ probe->domain);
+ return -EINVAL;
+ }
+ }
+ else
+ {
+ /* Default is to probe for ourselves. */
+ p = current;
+ get_task_struct(p); /* to mirror final put_task_struct */
+ }
- /* privileged domains always get access to the 'real' devices */
- if((ret = ide_probe_devices(&probe_params->xdi))) {
- printk("vbd_probe: error %d in probing ide devices\n", ret);
- goto out;
- }
- if((ret = scsi_probe_devices(&probe_params->xdi))) {
- printk("vbd_probe: error %d in probing scsi devices\n", ret);
- goto out;
- }
+ if ( (probe->domain == VBD_PROBE_ALL) || IS_PRIV(p) )
+ {
+ /* Privileged domains always get access to the 'real' devices. */
+ if ( (ret = ide_probe_devices(&probe->xdi)) != 0 )
+ {
+ DPRINTK("vbd_probe: error %d in probing ide devices\n", ret);
+ goto out;
+ }
+ if ( (ret = scsi_probe_devices(&probe->xdi)) != 0 )
+ {
+ DPRINTK("vbd_probe: error %d in probing scsi devices\n", ret);
+ goto out;
+ }
}
-
- if(!p) {
-
- u_long flags;
-
- read_lock_irqsave (&tasklist_lock, flags);
-
- p = &idle0_task;
- while ( (p = p->next_task) != &idle0_task ) {
- if (!is_idle_task(p)) {
- if((ret = vbd_probe_devices(&probe_params->xdi, p))) {
- printk("vbd_probe: error %d in probing virtual devices\n",
- ret);
- read_unlock_irqrestore(&tasklist_lock, flags);
- goto out;
- }
- }
- }
-
- read_unlock_irqrestore(&tasklist_lock, flags);
-
- } else {
-
- /* probe for disks and VBDs for just 'p' */
- if((ret = vbd_probe_devices(&probe_params->xdi, p))) {
- printk("vbd_probe: error %d in probing virtual devices\n", ret);
- goto out;
- }
+ if ( probe->domain == VBD_PROBE_ALL )
+ {
+ read_lock_irqsave(&tasklist_lock, flags);
+ p = &idle0_task;
+ while ( (p = p->next_task) != &idle0_task )
+ {
+ if ( !is_idle_task(p) )
+ {
+ if( (ret = vbd_probe_devices(&probe->xdi, p)) != 0 )
+ {
+ DPRINTK("vbd_probe: error %d in probing virtual devices\n",
+ ret);
+ read_unlock_irqrestore(&tasklist_lock, flags);
+ goto out;
+ }
+ }
+ }
+ read_unlock_irqrestore(&tasklist_lock, flags);
+ }
+ else
+ {
+ if ( (ret = vbd_probe_devices(&probe->xdi, p)) )
+ {
+ DPRINTK("vbd_probe: error %d in probing virtual devices\n", ret);
+ goto out;
+ }
}
out:
- if(putp)
- put_task_struct(p);
-
+ if ( p != NULL )
+ put_task_struct(p);
return ret;
}
-long vbd_info(vbd_info_t *info_params)
+
+long vbd_info(vbd_info_t *info)
{
- struct task_struct *p = NULL;
+ struct task_struct *p;
xen_extent_le_t *x;
xen_extent_t *extents;
vbd_t *v;
- int h, ret = 0;
+ long ret = 0;
- if(info_params->domain != current->domain && !IS_PRIV(current))
- return -EPERM;
-
- p = find_domain_by_id(info_params->domain);
-
- if (!p) {
- printk("vbd_info attempted for non-existent domain %d\n",
- info_params->domain);
- return -EINVAL;
+ if ( (info->domain != current->domain) && !IS_PRIV(current) )
+ return -EPERM;
+
+ if ( (p = find_domain_by_id(info->domain)) == NULL )
+ {
+ DPRINTK("vbd_info attempted for non-existent domain %d\n",
+ info->domain);
+ return -EINVAL;
}
- h = HSH(info_params->vdevice);
+ spin_lock(&p->vbd_lock);
- for(v = p->vbdtab[h]; v; v = v->next)
- if(v->vdevice == info_params->vdevice)
- break;
+ for ( v = p->vbdtab[HSH(info->vdevice)]; v != NULL; v = v->next )
+ if ( v->vdevice == info->vdevice )
+ break;
- if(!v) {
- printk("vbd_info attempted on non-existent VBD.\n");
- ret = -EINVAL;
- goto out;
+ if ( v == NULL )
+ {
+ DPRINTK("vbd_info attempted on non-existent VBD.\n");
+ ret = -EINVAL;
+ goto out;
}
- info_params->mode = v->mode;
- info_params->nextents = 0;
-
- extents = info_params->extents; // convenience
-
- for(x = v->extents; x; x = x->next) {
- if((ret = copy_to_user(extents++, &x->extent,
- sizeof(xen_extent_t))) < 0) {
- printk("vbd_info: copy_to_user failed [rc=%d]\n", ret);
- goto out;
- }
- info_params->nextents++;
+ info->mode = v->mode;
+ info->nextents = 0;
+
+ extents = info->extents;
+ for ( x = v->extents; x != NULL; x = x->next )
+ {
+ if ( copy_to_user(extents, &x->extent, sizeof(xen_extent_t)) )
+ {
+ DPRINTK("vbd_info: copy_to_user failed\n");
+ ret = -EFAULT;
+ goto out;
+ }
+ extents++;
+ info->nextents++;
}
out:
+ spin_unlock(&p->vbd_lock);
put_task_struct(p);
return ret;
}
-int vbd_translate(phys_seg_t * pseg, int *nr_segs,
- struct task_struct *p, int operation)
+int vbd_translate(phys_seg_t *pseg, struct task_struct *p, int operation)
{
xen_extent_le_t *x;
vbd_t *v;
- int h;
- long sec;
-
- h = HSH(pseg->dev);
-
- for(v = p->vbdtab[h]; v; v = v->next)
- if(v->vdevice == pseg->dev)
- break;
-
- if(!v) {
- if(!IS_PRIV(p))
- printk("vbd_translate; domain %d attempted to access "
- "non-existent VBD.\n", p->domain);
- return -ENODEV;
- }
-
- if(operation == READ && !VBD_CAN_READ(v))
- return -EACCES;
-
- if(operation == WRITE && !VBD_CAN_WRITE(v))
- return -EACCES;
-
+ unsigned long sec_off, nr_secs;
- /* Now iterate through the list of xen_extents, working out which
- should be used to perform the translation. */
- sec = pseg->sector_number;
- for(x = v->extents; x; x = x->next) {
+ spin_lock(&p->vbd_lock);
- if(sec < x->extent.nr_sectors) {
+ for ( v = p->vbdtab[HSH(pseg->dev)]; v != NULL; v = v->next )
+ if ( v->vdevice == pseg->dev )
+ goto found;
- /* we've got a match! XXX SMH: should deal with
- situation where we span multiple xe's */
+ if ( unlikely(!IS_PRIV(p)) )
+ DPRINTK("vbd_translate; domain %d attempted to access "
+ "non-existent VBD.\n", p->domain);
- pseg->dev = x->extent.device;
- pseg->sector_number += x->extent.start_sector;
+ spin_unlock(&p->vbd_lock);
+ return -ENODEV;
- return 0;
+ found:
- }
+ if ( ((operation == READ) && !VBD_CAN_READ(v)) ||
+ ((operation == WRITE) && !VBD_CAN_WRITE(v)) )
+ {
+ spin_unlock(&p->vbd_lock);
+ return -EACCES;
+ }
- sec -= x->extent.nr_sectors;
+ /*
+ * Now iterate through the list of xen_extents, working out which should
+ * be used to perform the translation.
+ */
+ sec_off = pseg->sector_number;
+ nr_secs = pseg->nr_sects;
+ for ( x = v->extents; x != NULL; x = x->next )
+ {
+ if ( sec_off < x->extent.nr_sectors )
+ {
+ pseg->dev = x->extent.device;
+ pseg->sector_number = x->extent.start_sector + sec_off;
+ if ( unlikely((sec_off + nr_secs) > x->extent.nr_sectors) )
+ goto overrun;
+ spin_unlock(&p->vbd_lock);
+ return 1;
+ }
+ sec_off -= x->extent.nr_sectors;
}
- /* No luck -- return no access */
+ DPRINTK("vbd_translate: end of vbd.\n");
+ spin_unlock(&p->vbd_lock);
return -EACCES;
-}
-
-
-
-
+ /*
+ * Here we deal with overrun onto the following extent. We don't deal with
+ * overrun of more than one boundary since each request is restricted to
+ * 2^9 512-byte sectors, so it should be trivial for control software to
+ * ensure that extents are large enough to prevent excessive overrun.
+ */
+ overrun:
+
+ /* Adjust length of first chunk to run to end of first extent. */
+ pseg[0].nr_sects = x->extent.nr_sectors - sec_off;
+
+ /* Set second chunk buffer and length to start where first chunk ended. */
+ pseg[1].buffer = pseg[0].buffer + (pseg[0].nr_sects << 9);
+ pseg[1].nr_sects = nr_secs - pseg[0].nr_sects;
+
+ /* Now move to the next extent. Check it exists and is long enough! */
+ if ( unlikely((x = x->next) == NULL) ||
+ unlikely(x->extent.nr_sectors < pseg[1].nr_sects) )
+ {
+ DPRINTK("vbd_translate: multiple overruns or end of vbd.\n");
+ spin_unlock(&p->vbd_lock);
+ return -EACCES;
+ }
+ /* Store the real device and start sector for the second chunk. */
+ pseg[1].dev = x->extent.device;
+ pseg[1].sector_number = x->extent.start_sector;
+
+ spin_unlock(&p->vbd_lock);
+ return 2;
+}